package com.li.friend.service.user.impl;

import cn.hutool.core.util.RandomUtil;
import cn.hutool.core.util.StrUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.li.common.core.constants.Constants;
import com.li.common.core.constants.HttpConstants;
import com.li.common.core.domain.LoginUser;
import com.li.common.core.domain.R;
import com.li.common.core.domain.vo.LoginUserVO;
import com.li.common.core.enums.ResultCode;
import com.li.common.core.enums.UserIdentity;
import com.li.common.core.utils.BCryptUtils;
import com.li.common.core.utils.ThreadLocalUtil;
import com.li.common.redis.service.RedisService;
import com.li.common.security.exception.ServiceException;
import com.li.common.security.service.TokenService;
import com.li.friend.domain.user.User;
import com.li.friend.domain.user.dto.UserDTO;
import com.li.friend.domain.user.dto.UserUpdateDTO;
import com.li.friend.domain.user.vo.UserVO;
import com.li.friend.manager.UserCacheManager;
import com.li.friend.mapper.user.UserMapper;
import com.li.friend.service.user.IUserService;
import com.li.friend.utils.Mail;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.autoconfigure.mail.MailProperties;
import org.springframework.mail.javamail.JavaMailSender;
import org.springframework.stereotype.Service;

import java.util.concurrent.TimeUnit;

@Service
@Slf4j
public class UserServiceImpl implements IUserService {

    @Autowired
    private UserMapper userMapper;

    @Autowired
    private RedisService redisService;

    @Autowired
    private TokenService tokenService;

    @Autowired
    private JavaMailSender javaMailSender;

    @Autowired
    private UserCacheManager userCacheManager;

    @Autowired
    private MailProperties mailProperties;

    @Value("${code-expriration:5}")
    private Long phoneCodeeExpriration;

    //邮件发送开关
    @Value("${sms.is-send:false}")
    private boolean isSend;
    @Value("${jwt.secret}")
    private String secret;

    @Value("${file.oss.downloadUrl}")
    private String downloadUrl;


    @Override
    public String sendCode(UserDTO userDTO) {
        // 判断用户是否存在，存在则不予许发送注册码
        User user = userMapper.selectOne(new LambdaQueryWrapper<User>()
                .eq(User::getEmail, userDTO.getEmail()));
        if(user != null){
            throw new ServiceException(ResultCode.AILED_USER_EXISTS);
        }
        // 判断验证码发送频率
        Long expire = redisService.getExpire("email:" + userDTO.getEmail(), TimeUnit.SECONDS);
        if(expire != null && (phoneCodeeExpriration * 60 - expire) < 60){
            throw new ServiceException(ResultCode.FAILED_FREQUENT);
        }

        String code = isSend ? RandomUtil.randomNumbers(6) : Constants.DEFAULT_CODE;
        //判断Redis里是否存在验证码，如果存在则删除，重新缓存验证码
        if(redisService.hasKey("email:" + userDTO.getEmail())){
            redisService.deleteObject("email:" + userDTO.getEmail());
        }
        String emailContent = buildContent(userDTO.getNickName(), code);
        if(isSend){
            sendNotificationEmail(userDTO.getEmail(), "OJ注册验证码", emailContent);
        }
        // 缓存验证码
        redisService.setCacheObject("email:" + userDTO.getEmail(), code, phoneCodeeExpriration, TimeUnit.MINUTES);

        return code;
    }

    //通过邮箱发送验证码
    @Override
    public int register(UserDTO userDTO) {
        //验证码校验正确后对密码加密，并保存用户信息,失败则返回错误验证码不正确
        String cacheCode = redisService.getCacheObject("email:" + userDTO.getEmail(), String.class);
        if(StrUtil.isEmpty(cacheCode)){
            throw new ServiceException(ResultCode.FAILED_INVALID_CODE);
        }
        if(!cacheCode.equals(userDTO.getCode())){
            throw new ServiceException(ResultCode.FAILED_ERROR_CODE);
        }
        redisService.deleteObject("email:" + userDTO.getEmail());
        //保存用户信息
        User user = new User();
        user.setPhone(userDTO.getPhone());
        user.setEmail(userDTO.getEmail());
        user.setPassword(BCryptUtils.encryptPassword(userDTO.getPassword()));
        return userMapper.insert(user);
    }

    @Override
    public R<String> login(String email, String password) {
        LambdaQueryWrapper<User> queryWrapper = new LambdaQueryWrapper<>();
        User user = userMapper.selectOne(queryWrapper
                .select(User::getUserId,User::getPassword, User::getNickName, User::getHeadImage)
                .eq(User::getEmail, email));
        if(user == null) {
            log.info("用户：" + email + "不存在");
            return R.fail(ResultCode.FAILED_USER_NOT_EXISTS);
        }
        if(BCryptUtils.matchesPassword(password,user.getPassword())) {
            log.info("用户" + email + "登录成功");
            //令牌逻辑封装便于复用
            return R.ok(tokenService.createToken(user.getUserId(),secret, UserIdentity.ORDINARY.getValue(), user.getNickName(),user.getHeadImage()));
        }
        log.info("用户：" + email + "密码输入错误");
        return R.fail(ResultCode.FAILED_LOGIN);
    }

    @Override
    public boolean logout(String token) {
        if (StrUtil.isNotEmpty(token) && token.startsWith(HttpConstants.PREFIX)) {
            token = token.replaceFirst(HttpConstants.PREFIX, StrUtil.EMPTY);
        }
        return tokenService.deleteLoginUser(token, secret);
    }

    @Override
    public R<LoginUserVO> info(String token) {
        if (StrUtil.isNotEmpty(token) && token.startsWith(HttpConstants.PREFIX)) {
            token = token.replaceFirst(HttpConstants.PREFIX, StrUtil.EMPTY);
        }
        LoginUser loginUser = tokenService.getLoginUser(token, secret);
        if (loginUser == null) {
            return R.fail();
        }
        LoginUserVO loginUserVO = new LoginUserVO();
        loginUserVO.setNickName(loginUser.getNickName());
        if (StrUtil.isNotEmpty(loginUser.getHeadImage())) {
            loginUserVO.setHeadImage(downloadUrl + loginUser.getHeadImage());
        }
        return R.ok(loginUserVO);
    }

    @Override
    public UserVO detail() {
        Long userId = ThreadLocalUtil.get(Constants.USER_ID, Long.class);
        if (userId == null) {
            throw new ServiceException(ResultCode.FAILED_USER_NOT_EXISTS);
        }
        UserVO userVO = userCacheManager.getUserById(userId);
        if (userVO == null) {
            throw new ServiceException(ResultCode.FAILED_USER_NOT_EXISTS);
        }
        if (StrUtil.isNotEmpty(userVO.getHeadImage())) {
            userVO.setHeadImage(downloadUrl + userVO.getHeadImage());
        }
        return userVO;
    }

    @Override
    public int edit(UserUpdateDTO userUpdateDTO) {
        Long userId = ThreadLocalUtil.get(Constants.USER_ID, Long.class);
        if (userId == null) {
            throw new ServiceException(ResultCode.FAILED_USER_NOT_EXISTS);
        }
        User user = userMapper.selectById(userId);
        if (user == null) {
            throw new ServiceException(ResultCode.FAILED_USER_NOT_EXISTS);
        }
        user.setNickName(userUpdateDTO.getNickName());
        user.setSex(userUpdateDTO.getSex());
        user.setSchoolName(userUpdateDTO.getSchoolName());
        user.setMajorName(userUpdateDTO.getMajorName());
        user.setPhone(userUpdateDTO.getPhone());
        user.setEmail(userUpdateDTO.getEmail());
        user.setWechat(userUpdateDTO.getWechat());
        user.setIntroduce(userUpdateDTO.getIntroduce());
        //更新用户缓存
        userCacheManager.refreshUser(user);
        tokenService.refreshLoginUser(user.getNickName(),user.getHeadImage(),
                ThreadLocalUtil.get(Constants.USER_KEY, String.class));
        return userMapper.updateById(user);
    }

    @Override
    public int updateHeadImage(String headImage) {
        Long userId = ThreadLocalUtil.get(Constants.USER_ID, Long.class);
        if (userId == null) {
            throw new ServiceException(ResultCode.FAILED_USER_NOT_EXISTS);
        }
        User user = userMapper.selectById(userId);
        if (user == null) {
            throw new ServiceException(ResultCode.FAILED_USER_NOT_EXISTS);
        }
        user.setHeadImage(headImage);
        //更新用户缓存
        userCacheManager.refreshUser(user);
        tokenService.refreshLoginUser(user.getNickName(),user.getHeadImage(),
                ThreadLocalUtil.get(Constants.USER_KEY, String.class));
        return userMapper.updateById(user);
    }


    //构建邮件内容
    private String buildContent(String nickName,String  code){
        StringBuilder builder = new StringBuilder();
        builder.append("尊敬的").append(nickName).append(", 您好!").append("<br/>");
        builder.append("感谢您注册成为我们致科OJ社区的一员! 我们很高新您加入我们的大家庭!<br/>");
        builder.append("您的注册信息如下: 用户名: ").append(nickName).append("<br/>");
        builder.append("您的验证码是: ").append(code).append("<br/>");
        builder.append("为了确保您的账⼾安全，请妥善保管您的登录信息. 如果使⽤过程中, 遇到任何问题, 欢迎联系我沟通. lff040612@163.com <br/>");
        builder.append("再次感谢您的加⼊，我们期待看到您的精彩内容！<br/>")
                .append("最好的祝愿<br/>")
                .append("致科OJ-小李").toString();
        return builder.toString();
    }

    /**
     *
     * @param recipient 用户邮箱
     * @param subject 邮件主题
     * @param content 邮件内容
     */
    private void sendNotificationEmail(String recipient, String subject, String content) {
        Mail mail = new Mail(javaMailSender, mailProperties);
        try {
            mail.send(recipient, subject, content);
        } catch (Exception e) {
            log.error("邮件发送失败", e);
            throw new ServiceException(ResultCode.FAILED_SEND_CODE);
        }
    }
}
